import * as server from "@minecraft/server";
import * as data_entity from "../data/entity";
export { Vector, QueryFoothold, QueryEntityFoothold, Clamp, Random, NumberID };
class Vector {
    static equals(start, done) {
        return start.x === done.x && start.y === done.y && start.z === done.z;
    }
    ;
    static copy(vector) {
        return this.add(vector, this.CONSTANT_ZERO);
    }
    ;
    static add(start, done) {
        return { x: start.x + done.x, y: start.y + done.y, z: start.z + done.z };
    }
    ;
    static subtract(start, done) {
        return { x: start.x - done.x, y: start.y - done.y, z: start.z - done.z };
    }
    ;
    static scale(vector, scale) {
        return { x: vector.x * scale, y: vector.y * scale, z: vector.z * scale };
    }
    ;
    static dot(start, done) {
        return start.x * done.x + start.y * done.y + start.z * done.z;
    }
    ;
    static cross(start, done) {
        return {
            x: start.y * done.z - start.z * done.y,
            y: start.z * done.x - start.x * done.z,
            z: start.x * done.y - start.y * done.x,
        };
    }
    ;
    static division(vector, divisor) {
        if (divisor === 0)
            return vector;
        return {
            x: vector.x / divisor,
            y: vector.y / divisor,
            z: vector.z / divisor
        };
    }
    ;
    static magnitude(vector) {
        return Math.sqrt(vector.x ** 2 + vector.y ** 2 + vector.z ** 2);
    }
    ;
    static distance(start, done) {
        return this.magnitude(this.subtract(start, done));
    }
    ;
    static normalize(vector) {
        const mag = this.magnitude(vector);
        return { x: vector.x / mag, y: vector.y / mag, z: vector.z / mag };
    }
    ;
    static floor(vector, decimals = 2) {
        const multiplier = Math.pow(10, decimals);
        return {
            x: Math.floor(vector.x * multiplier) / multiplier,
            y: Math.floor(vector.y * multiplier) / multiplier,
            z: Math.floor(vector.z * multiplier) / multiplier
        };
    }
    ;
    static toString(vector, options) {
        const decimals = options?.decimals ?? 2;
        const delimiter = options?.delimiter ?? ', ';
        const components = 'z' in vector
            ? [vector.x.toFixed(decimals), vector.y.toFixed(decimals), vector.z.toFixed(decimals)]
            : [vector.x.toFixed(decimals), vector.y.toFixed(decimals)];
        return components.join(delimiter);
    }
    ;
    static clamp(vector, limits) {
        return {
            x: Clamp({ min: limits?.min?.x ?? Number.MIN_SAFE_INTEGER, max: limits?.max?.x ?? Number.MAX_SAFE_INTEGER }, vector.x),
            y: Clamp({ min: limits?.min?.y ?? Number.MIN_SAFE_INTEGER, max: limits?.max?.y ?? Number.MAX_SAFE_INTEGER }, vector.y),
            z: Clamp({ min: limits?.min?.z ?? Number.MIN_SAFE_INTEGER, max: limits?.max?.z ?? Number.MAX_SAFE_INTEGER }, vector.z),
        };
    }
    ;
    static lerp(start, done, time) {
        return {
            x: start.x + (done.x - start.x) * time,
            y: start.y + (done.y - start.y) * time,
            z: start.z + (done.z - start.z) * time,
        };
    }
    ;
    static slerp(start, done, time) {
        const angleCosine = this.dot(start, done);
        const safeAngleCosine = Math.min(Math.max(angleCosine, -1), 1);
        const angleTheta = Math.acos(safeAngleCosine);
        const angleSin = Math.sin(angleTheta);
        if (Math.abs(angleSin) < Number.EPSILON)
            return start;
        const ratioA = Math.sin((1.0 - time) * angleTheta) / angleSin;
        const ratioB = Math.sin(time * angleTheta) / angleSin;
        return this.add(this.scale(start, ratioA), this.scale(done, ratioB));
    }
    ;
    static random(start, done) {
        const range = new server.BlockVolume(start, done);
        const min = range.getMin();
        const max = range.getMax();
        return {
            x: Random({ min: min.x, max: max.x }),
            y: Random({ min: min.y, max: max.y }),
            z: Random({ min: min.z, max: max.z })
        };
    }
    ;
    static difference(start, done) {
        const direction = this.subtract(done, start);
        return this.normalize(direction);
    }
    ;
    static AngleToPlace(rotate) {
        const ry = -rotate.y * Math.PI / 180;
        const rx = -rotate.x * Math.PI / 180;
        const x = Math.sin(ry) * Math.cos(rx);
        const y = Math.sin(rx);
        const z = Math.cos(ry) * Math.cos(rx);
        return { x, y, z };
    }
    ;
    static directions(front) {
        const sample = this.CONSTANT_UP;
        const back = this.normalize(this.scale(front, -1));
        const right = this.normalize(this.cross(front, sample));
        const left = this.normalize(this.scale(right, -1));
        const down = this.normalize(this.cross(front, right));
        const above = this.normalize(this.scale(down, -1));
        return { front, back, left, right, above, down };
    }
    ;
    static relativeOffset(source, front, offset) {
        const directions = this.directions(front);
        const frontScale = this.scale(front, offset.front);
        const rightScale = this.scale(directions.right, offset.right);
        const upScale = this.scale(directions.above, offset.above);
        return this.add(source, this.add(frontScale, this.add(upScale, rightScale)));
    }
    ;
    static calculateLeadVelocity(posA, posB, speedA, velB) {
        const vecBA = this.subtract(posB, posA);
        const normVecBA = this.normalize(vecBA);
        const compVbBA = this.dot(velB, normVecBA);
        const vbAlongBA = this.scale(normVecBA, compVbBA);
        const perpCompVa = this.subtract(velB, vbAlongBA);
        const magVaBA = Math.sqrt(speedA * speedA - this.dot(perpCompVa, perpCompVa));
        if (magVaBA <= 0)
            return perpCompVa;
        const vaAlongBA = this.scale(normVecBA, -magVaBA);
        return this.add(perpCompVa, vaAlongBA);
    }
    ;
    static chunkLocation(vector, Yzero = true, size = 16) {
        const y = Yzero ? 0 : vector.y;
        return { x: Math.floor(vector.x / size) * size, y, z: Math.floor(vector.z / size) * size };
    }
    ;
    static get CONSTANT_HALF() { return { x: 0.5, y: 0.5, z: 0.5 }; }
    ;
    static get CONSTANT_ZERO() { return { x: 0, y: 0, z: 0 }; }
    ;
    static get CONSTANT_UP() { return { x: 0, y: 1, z: 0 }; }
    ;
    static get CONSTANT_DOWN() { return { x: 0, y: -1, z: 0 }; }
    ;
    static get CONSTANT_ONE() { return { x: 1, y: 1, z: 1 }; }
    ;
    static get CONSTANT_LOSS_ONE() { return { x: -1, y: -1, z: -1 }; }
    ;
    static get CONSTANT_WEST() { return { x: -1, y: 0, z: 0 }; }
    ;
    static get CONSTANT_EAST() { return { x: 1, y: 0, z: 0 }; }
    ;
    static get CONSTANT_SOUTH() { return { x: 0, y: 0, z: 1 }; }
    ;
    static get CONSTANT_NORTH() { return { x: 0, y: 0, z: -1 }; }
    ;
    constructor() { }
    ;
}
;
function QueryFoothold(source, range, height, limit) {
    let output = Vector.CONSTANT_ZERO;
    const maxRepeat = 1024;
    let alpha = 1;
    while (alpha > 0 && alpha <= maxRepeat) {
        const random0 = Random({ max: 3, min: 0 }, true);
        const random1 = Random({ max: range, min: 16 }, true);
        const random2 = Random({ max: -16, min: -range }, true);
        output =
            {
                x: source.location.x + (random0 === 0 || random0 === 2 ? random1 : random2),
                y: Random({ max: limit, min: height }, true),
                z: source.location.z + (random0 === 0 || random0 === 3 ? random1 : random2)
            };
        const getBlock0 = source.dimension.getBlock(output);
        const getBlock1 = source.dimension.getBlock(Vector.add(output, this.CONSTANT_UP));
        const getBlock2 = source.dimension.getBlock(Vector.add(output, { x: 0, y: -3, z: 0 }));
        const getBlock3 = source.dimension.getBlock(Vector.add(output, { x: 8, y: -2, z: 8 }));
        const getBlock4 = source.dimension.getBlock(Vector.add(output, { x: -8, y: -4, z: -8 }));
        if (getBlock0?.isAir && getBlock1?.isAir && getBlock2 && getBlock3 && getBlock4) {
            const test2 = getBlock2.isSolid;
            const test3 = getBlock3.isSolid;
            const test4 = getBlock4.isSolid;
            if (test2 && test3 && test4)
                alpha = -1;
        }
        alpha++;
    }
    return alpha == maxRepeat ? source.location : output;
}
;
function QueryEntityFoothold(source, scale, range) {
    const options = {
        excludeTypes: [...data_entity.area_legend.keys(), "minecraft:player"],
        location: source.location,
        maxDistance: range,
        closest: scale
    };
    const entitys = source.dimension.getEntities(options);
    if (entitys.length < 1)
        return Vector.add(source.location, { x: 0, y: 255, z: 0 });
    const blocks = entitys.map(entity => entity.dimension.getBlock(entity.location));
    if (entitys.length < 1)
        return Vector.add(source.location, { x: 0, y: 128, z: 0 });
    const output = blocks.filter(block => {
        if (!block)
            return false;
        const above = block.above();
        const below = block.below();
        const east = below?.east();
        const south = below?.south();
        if (!above?.isAir && !above?.isLiquid)
            return false;
        if (below?.isAir || below?.isLiquid)
            return false;
        if (south?.isAir || south?.isLiquid)
            return false;
        if (east?.isAir || east?.isLiquid)
            return false;
        return true;
    });
    output.sort((a, b) => {
        const distance_a = Vector.distance(source.location, a.location);
        const distance_b = Vector.distance(source.location, b.location);
        return distance_a - distance_b;
    });
    if (output.length < 1)
        return Vector.add(source.location, { x: 0, y: 64, z: 0 });
    else
        return output[0].location;
}
;
function Clamp(input, test) {
    return Math.max(input.min, Math.min(input.max, test));
}
;
function Random(input, integer) {
    if (integer == true) {
        return Math.floor(Math.random() * (input.max - input.min + 1) + input.min);
    }
    else {
        return Math.random() * (input.max - input.min) + input.min;
    }
}
;
function median(input) {
    const price = input.sort((a, b) => a - b);
    const index = Math.floor(price.length / 2);
    return price.length % 2 === 0
        ? (price[index - 1] + price[index]) / 2
        : price[index];
}
;
function mode(input) {
    const frequency = new Map();
    let mode = [];
    let maxFrequency = 0;
    input.forEach(num => {
        if (frequency.has(num)) {
            frequency.set(num, frequency.get(num) + 1);
        }
        else {
            frequency.set(num, 1);
        }
    });
    frequency.forEach((frequency, num) => {
        if (frequency > maxFrequency) {
            maxFrequency = frequency;
            mode = [num];
        }
        else if (frequency === maxFrequency) {
            mode.push(num);
        }
    });
    return mode;
}
;
function NumberID(deplete) {
    const change = deplete ?? server.system.currentTick;
    return Number.MAX_SAFE_INTEGER - change;
}
;
;
;
;
;
;
